/*
* =====================================================================================
*
*       Filename:  mainWindow.cpp
*
*        Version:  2.3
*        Created:  09/11/13 12:50:50
*
*         Author:  LIU RENSHENG (A0105649B), roastedbill@gmail.com
*   Organization:  NUS, SoC
*
* =====================================================================================
*/

#include "mainWindow.h"

//@author A0105649B
mainWindow::mainWindow(QWidget *parent)
	: QMainWindow(parent)//@ A0105649B
{
	ui.setupUi(this);
	changeUIStyle();
	setUpFormateModifier();

	connectSingleKeyEvents();
	connectMultiKeyEvents();

	initializeParameters();
	displayDefaultContent();
	return;
}

mainWindow::~mainWindow(){
}

void mainWindow::processing(){//@ A0105649B
	triggerFeedback(executeCommand(getCommandLine()));
	displayDefaultContent();
	clearLineEditTextFormat(ui.InputLine); //set format back to default
	return;
}

void mainWindow::changeTextAttribute(){//@ A0105649B
	QString buffer = ui.InputLine->text().toLower(); //Case Insensitive 

	if(buffer.mid(BASE,KEYWORD_LENGTH6) == CREATE_KEYWORD1){
		setKeywordFormatCreate();
	}
	else if (buffer == CREATE_KEYWORD2){
		setKeywordCreate();
		setKeywordFormatCreate();
	}
	else if(buffer.mid(BASE,KEYWORD_LENGTH7) == DISPLAY_KEYWORD1){
		setKeywordFormatDisplay();
	}
	else if(buffer == DISPLAY_KEYWORD2){
		setKeywordDisplay();
		setKeywordFormatDisplay();
	}
	else if(buffer.mid(BASE,KEYWORD_LENGTH6) == DELETE_KEYWORD1){
		setKeywordFormatDelete();
	}
	else if(buffer == DELETE_KEYWORD2){
		setKeywordDelete();
		setKeywordFormatDelete();
	}
    else if(buffer.mid(BASE,KEYWORD_LENGTH6) == UPDATE_KEYWORD1){
		setKeywordFormatUpdate();
	}
	else if(buffer == UPDATE_KEYWORD2){
		setKeywordUpdate();
		setKeywordFormatUpdate();
	}
	else if(buffer.mid(BASE,KEYWORD_LENGTH6) == SEARCH_KEYWORD1){
		setKeywordFormatSearch();
	}
	else if(buffer == SEARCH_KEYWORD2){
		setKeywordSearch();
		setKeywordFormatSearch();
	}

	else if(buffer.mid(BASE,KEYWORD_LENGTH4) == UNDO_KEYWORD){
		setKeywordFormatUndo();
	}
	else if(buffer.mid(BASE,KEYWORD_LENGTH4) == REDO_KEYWORD){
		setKeywordFormatRedo();
	}
	else if(buffer.mid(BASE,KEYWORD_LENGTH4) == MARK_KEYWORD){
		setKeywordFormatMark();
	}
	else if(buffer.mid(BASE,KEYWORD_LENGTH6) == UNMARK_KEYWORD){
		setKeywordFormatUnmark();
	}
	else if(buffer.mid(BASE,KEYWORD_LENGTH4) == EXIT_KEYWORD1){
		setKeywordFormatExit();
	}
	else if(buffer == EXIT_KEYWORD2){
		setKeywordExit();
		setKeywordFormatExit();
	}
	else if(buffer.length() <= MAX_KEY_LENGTH){ // not a key word, might due to deleting some charactors from a key word
		clearLineEditTextFormat(ui.InputLine);
		clearIsOnlyKeyword();
		displayDefaultContent();
	}
	else{
		clearIsOnlyKeyword();
		displayDefaultContent();
	}
	return;
}

void mainWindow::setupFileStream(string fileName){//@ A0105649B
	logic.initializeFileStream(fileName);
	return;
}

void mainWindow::changeUIStyle(){//@ A0105649B
	setMainWidgetStyle();
	setComponentsStyle();
	return;
}

void mainWindow::setMainWidgetStyle(){//@ A0105649B
	this->setWindowFlags(Qt::FramelessWindowHint);
	this->setStyleSheet(CENTRAL_WIDGET_STYLE);
	this->setAttribute(Qt::WA_TranslucentBackground);
	this->setWindowIcon(QIcon(ICON_NAME_STRING));
	setupTrayIcon();
	this->move(INITIAL_MAIN_POSX, INITIAL_MAIN_POSY);
	return;
}

void mainWindow::setupTrayIcon(){//@ A0105649B
        trayIcon = new QSystemTrayIcon(this);
        trayIcon->setIcon(QIcon(TRAY_ICON_PATH));
        trayIcon->show();
        trayIcon->setToolTip(TOOL_TIP);
}

void mainWindow::setComponentsStyle(){//@ A0105649B
	ui.InputLine->setStyleSheet(INPUT_LINE_STYLE);
	ui.InputLine->setAttribute(Qt::WA_TranslucentBackground);
	ui.defaultFeedback->viewport()->setAutoFillBackground(false);
	return;
}

void mainWindow::connectSingleKeyEvents(){//@ A0105649B
	connect(ui.InputLine, SIGNAL(returnPressed()), this, SLOT(processing()));
	connect(ui.InputLine, SIGNAL(textEdited(QString)), this, SLOT(changeTextAttribute()));
	connect(ui.closeButton, SIGNAL(clicked()), this, SLOT(handleQuit()));
	connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconIsActived(QSystemTrayIcon::ActivationReason)));
	return;
}

void mainWindow::iconIsActived(QSystemTrayIcon::ActivationReason){//@ A0105649B
	this->show();
	return;
}

void mainWindow::connectMultiKeyEvents(){//@ A0105649B
	(void) new QShortcut(QKeySequence(tr("Ctrl+Q")), this, SLOT(handleQuit()));
	(void) new QShortcut(QKeySequence(tr("Alt+Esc")), this, SLOT(handleQuit()));
	(void) new QShortcut(QKeySequence(tr("Ctrl+M")), this, SLOT(handleKeyCtrlM()));
	(void) new QShortcut(QKeySequence(tr("Ctrl+D")), this, SLOT(handleKeyCtrlD()));
	(void) new QShortcut(QKeySequence(tr("Ctrl+Tab")), this, SLOT(handleKeyCtrlTab()));
	return;
}

void mainWindow::initializeParameters(){//@ A0105649B
	setupFileStream(q2s(FILE_NAME));
	initializeRelativePos();
	clearIsOnlyKeyword();
	return;
}

void mainWindow::displayDefaultContent(){//@ A0105649B
	DisplayCommand defaultContent = DisplayCommand(DEFAULT_DISPLAY_STRING);
	ui.defaultFeedback->setText(s2q(defaultContent.Execute())); 
	return;
}

QString mainWindow::getCommandLine(){//@ A0105649B
	resetRecord();
	QString temp = ui.InputLine->text();
	saveToRecord(temp);
	return temp;
}

std::string mainWindow::executeCommand(QString userInput){//@ A0105649B
	std::string input = q2s(handleMarkUnmark(userInput));
	return logic.executeCommand(input);
}

QString mainWindow::handleMarkUnmark(QString userInput){//@ A0105649B
	if(userInput.mid(BASE,KEYWORD_LENGTH4) == MARK_KEYWORD){
	    userInput = UPDATE_KEYWORD1 + SPACE + userInput.split(SPACE)[INDEX_PART] + TRANSLATE_MARK; 
	}
	else if(userInput.mid(BASE,KEYWORD_LENGTH6) == UNMARK_KEYWORD){
	    userInput = UPDATE_KEYWORD1 + SPACE + userInput.split(SPACE)[INDEX_PART] + TRANSLATE_UNMARK;
	}
	return userInput;
}

void mainWindow::triggerFeedback(std::string feedbackText){//@ A0105649B
	QString QFeedback = s2q(feedbackText);
	if(QFeedback == EXIT_FEEDBACK){
		handleQuit();
	}
	resetMainWindow();
	generateFeedbackNote(QFeedback);
	return;
}

void mainWindow::resetMainWindow(){//@ A0105649B
	ui.InputLine->clear();
	clearLineEditTextFormat(ui.InputLine);
	displayDefaultContent();
	return;
}

void mainWindow::generateFeedbackNote(QString QFeedback){//@ A0105649B
	feedbackWindow *feedbackwindow = new feedbackWindow(QFeedback, this->pos(), relativePos, this);
	modifyRelativePos();
	feedbackwindow -> show();
	return;
}

void mainWindow::keyPressEvent(QKeyEvent* event){//@ A0105649B
	if(event->key() == Qt::Key_Down){
		this->handleKeyDown();
	}

	else if(event->key() == Qt::Key_Up){//@ A0105649B
		this->handleKeyUp();
	}
	return;
}

void mainWindow::handleKeyCtrlD(){//@ A0105649B
	resetMainWindow();
	return;
}

void mainWindow::handleKeyCtrlM(){//@ A0105649B
	QTimer::singleShot(RIGHT_AWAY, this, SLOT(hide()));
}

void mainWindow::handleKeyUp()
{
	if(inputRecordIn.size() > SINGLE){ //assertion
		showLastRecord(&inputRecordIn,&inputRecordOut);
	}

	else if(inputRecordIn.size() == SINGLE){ //no need to pop out for the last one
		showLastRecordWithoutPop(&inputRecordIn,&inputRecordOut);
	}
	changeTextAttribute();
	return;
}

void mainWindow::handleKeyDown()//@ A0105649B
{
	if(inputRecordOut.size() > SINGLE)
	{
		showLastRecord(&inputRecordOut,&inputRecordIn);
	}

	else if(inputRecordOut.size() == SINGLE){
		showLastRecordWithoutPop(&inputRecordOut,&inputRecordIn);
	}
	changeTextAttribute();
	return;
}

void mainWindow::showLastRecord(QStack<QString> *fromStack,QStack<QString> *toStack){//@ A0105649B
	QString lastInput = fromStack->pop();
	toStack->push(lastInput);
	resetMainWindow();
	ui.InputLine->setText(lastInput);
	return;
}

void mainWindow::showLastRecordWithoutPop(QStack<QString> *fromStack,QStack<QString> *toStack){//@ A0105649B
	QString lastInput = fromStack->top();
	resetMainWindow();
	ui.InputLine->setText(lastInput);
	return;
}

void mainWindow::handleQuit(){//@ A0105649B
	saveToFile();
	qApp->quit();
	return;
}

void mainWindow::saveToFile(){//@ A0105649B
	logic.writeToFile();
	return;
}

void mainWindow::handleKeyCtrlTab(){//@ A0105649B
	if (isOnlyKeyword){
		clearLineEditTextFormat(ui.InputLine);
		if(ui.InputLine->text() == DISPLAY_CREATE_KEYWORD){
			setKeywordDisplay();
			setKeywordFormatDisplay();
		}
		else if(ui.InputLine->text() == DISPLAY_DISPLAY_KEYWORD){
			setKeywordDelete();
			setKeywordFormatDelete();
		}	
		else if(ui.InputLine->text() == DISPLAY_DELETE_KEYWORD){
			setKeywordUpdate();
			setKeywordFormatUpdate();
		}
		else if(ui.InputLine->text() == DISPLAY_UPDATE_KEYWORD){
			setKeywordSearch();
			setKeywordFormatSearch();
		}
		else if(ui.InputLine->text() == DISPLAY_SEARCH_KEYWORD){
			setKeywordUndo();
			setKeywordFormatUndo();
		}
		else if(ui.InputLine->text() == DISPLAY_UNDO_KEYWORD){
			setKeywordRedo();
			setKeywordFormatRedo();
		}
		else if(ui.InputLine->text() == DISPLAY_REDO_KEYWORD){
			setKeywordMark();
			setKeywordFormatMark();
		}
		else if(ui.InputLine->text() == DISPLAY_MARK_KEYWORD){
			setKeywordUnmark();
			setKeywordFormatUnmark();
		}
		else if(ui.InputLine->text() == DISPLAY_UNMARK_KEYWORD){
			setKeywordExit();
			setKeywordFormatExit();
		}
		else if(ui.InputLine->text() == DISPLAY_EXIT_KEYWORD){
			setKeywordCreate();
			setKeywordFormatCreate();
		}
	}
	return;
}

void mainWindow::setKeywordCreate(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_CREATE_KEYWORD);
	return;
}

void mainWindow::setKeywordDisplay(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_DISPLAY_KEYWORD);
	return;
}

void mainWindow::setKeywordDelete(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_DELETE_KEYWORD);
	return;
}

void mainWindow::setKeywordUpdate(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_UPDATE_KEYWORD);
	return;
}

void mainWindow::setKeywordSearch(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_SEARCH_KEYWORD);
	return;
}

void mainWindow::setKeywordUndo(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_UNDO_KEYWORD);
	return;
}

void mainWindow::setKeywordRedo(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_REDO_KEYWORD);
	return;
}

void mainWindow::setKeywordMark(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_MARK_KEYWORD);
	return;
}

void mainWindow::setKeywordUnmark(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_UNMARK_KEYWORD);
	return;
}


void mainWindow::setKeywordExit(){//@ A0105649B
	ui.InputLine->clear();
	ui.InputLine->setText(DISPLAY_EXIT_KEYWORD);
	return;
}

void mainWindow::constructLayoutFormat(int length, QTextCharFormat format){//@ A0105649B
	formats.clear();
	QTextLayout::FormatRange range;
	range.start = BASE;
	range.length = length;
	range.format = format;
	formats.append(range);
	return;
}

void mainWindow::setKeywordFormatCreate(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH6, createFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	bumpOutSuggestionCreate();
	setIsOnlyKeyword();
	return;
}

void mainWindow::setKeywordFormatDisplay(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH7, displayFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	bumpOutSuggestionDisplay();
	setIsOnlyKeyword();
	return;
}

void mainWindow::setKeywordFormatDelete(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH6, deleteFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	bumpOutSuggestionDelete();
	setIsOnlyKeyword();
	return;
}

void mainWindow::setKeywordFormatUpdate(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH6, updateFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	bumpOutSuggestionUpdate();
	setIsOnlyKeyword();
	return;
}

void mainWindow::setKeywordFormatSearch(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH6, searchFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	bumpOutSuggestionSearch();
	setIsOnlyKeyword();
	return;
}

void mainWindow::setKeywordFormatUndo(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH4, undoFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	setIsOnlyKeyword();
	bumpOutSuggestionUndo();
	return;
}

void mainWindow::setKeywordFormatRedo(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH4, redoFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	setIsOnlyKeyword();
	bumpOutSuggestionRedo();
	return;
}

void mainWindow::setKeywordFormatMark(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH4, markFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	setIsOnlyKeyword();
	bumpOutSuggestionMark();
	return;
}

void mainWindow::setKeywordFormatUnmark(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH6, unmarkFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	setIsOnlyKeyword();
	bumpOutSuggestionUnmark();
	return;
}

void mainWindow::setKeywordFormatExit(){//@ A0105649B
	constructLayoutFormat(KEYWORD_LENGTH4, exitFormat);
	setLineEditTextFormat(ui.InputLine, formats);
	setIsOnlyKeyword();
	bumpOutSuggestionExit();
	return;
}

void mainWindow::saveToRecord(QString record){//@ A0105649B
	if(record.length()){ 
		this -> pushToRecordStack(record);
		deleteRepeatedRecord(record);
	}
	return;
}

void mainWindow::pushToRecordStack(QString record){//@ A0105649B
	this->inputRecordIn.push(record);
	return;
}

void mainWindow::resetRecord(){//@ A0105649B
	if(!inputRecordOut.isEmpty()){
		while(!inputRecordOut.isEmpty()){
			inputRecordIn.push(inputRecordOut.pop());
		}
	}
	return;
}

void mainWindow::deleteRepeatedRecord(QString record){//@ A0105649B
	if(inputRecordIn.size() > SINGLE){ //assertion
		deleteRecordFromStack(record);
	}
	return;
}

void mainWindow::deleteRecordFromStack(QString record){//@ A0105649B
	for(int index=inputRecordIn.size()-2; index>=BASE; index--){
		if(inputRecordIn[index] == record){
			inputRecordIn.remove(index); 
			break;
		}
	}
	return;
}

void mainWindow::setUpFormateModifier(){//@ A0105649B
	constructFormat(createFormat);
	constructFormat(displayFormat);
	constructFormat(deleteFormat);
	constructFormat(updateFormat);
	constructFormat(searchFormat);
	constructFormat(undoFormat);
	constructFormat(redoFormat);
	constructFormat(markFormat);
	constructFormat(unmarkFormat);
	constructFormat(exitFormat);
	return;
}

void mainWindow::constructFormat(QTextCharFormat &format){//@ A0105649B
	if(format == createFormat){
	    format.setForeground(Qt::blue);
	}
	else if(format == displayFormat){
	    format.setForeground(Qt::darkCyan);
	}
	else if(format == deleteFormat){
	    format.setForeground(Qt::red);
	}
	else if(format == updateFormat){
	    format.setForeground(Qt::green);
	}
	else if(format == searchFormat){
	    format.setForeground(Qt::gray);
	}
	else if(format == undoFormat){
	    format.setForeground(Qt::darkGreen);
	}
	else if(format == redoFormat){
	    format.setForeground(Qt::darkMagenta);
	}
	else if(format == markFormat){
	    format.setForeground(Qt::darkGray);
	}
	else if(format == unmarkFormat){
	    format.setForeground(Qt::darkBlue);
	}
	else if(format == exitFormat){
	    format.setForeground(Qt::darkRed);
	}
	format.setFontWeight(QFont::Bold);
	format.setFontItalic(true);
	return;
}

void mainWindow::setLineEditTextFormat(QLineEdit* lineEdit, const QList<QTextLayout::FormatRange>& formats)//@ A0105649B
{
	if(!lineEdit)
		return;

	QList<QInputMethodEvent::Attribute> attributes;
	foreach(const QTextLayout::FormatRange& fr, formats)
	{
		QInputMethodEvent::AttributeType type = QInputMethodEvent::TextFormat;
		int start = fr.start - lineEdit->cursorPosition();
		int length = fr.length;
		QVariant value = fr.format;
		attributes.append(QInputMethodEvent::Attribute(type, start, length, value));
	}
	QInputMethodEvent event(QString(), attributes);
	QCoreApplication::sendEvent(lineEdit, &event);
	return;
}

void mainWindow::clearLineEditTextFormat(QLineEdit* lineEdit)//@ A0105649B
{
	setLineEditTextFormat(lineEdit, QList<QTextLayout::FormatRange>());
	return;
}

void mainWindow::bumpOutSuggestionCreate(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_CREATE_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionDisplay(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_DISPLAY_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionDelete(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_DELETE_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionUpdate(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_UPDATE_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionSearch(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_SEARCH_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionUndo(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_UNDO_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionRedo(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_REDO_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionMark(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_MARK_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionUnmark(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_UNMARK_FEEDBACK);
	return;
}

void mainWindow::bumpOutSuggestionExit(){//@ A0105649B
	ui.defaultFeedback->setText(DISPLAY_EXIT_FEEDBACK);
	return;
}

QString mainWindow::s2q(const std::string &s)  { //@ A0105649B 
	return QString(QString::fromLocal8Bit(s.c_str()));  
}  

std::string mainWindow::q2s(const QString &s)  {  //@ A0105649B
	return std::string((const char *)s.toLocal8Bit());  
}  

void mainWindow::mousePressEvent(QMouseEvent *event){//@ A0105649B                                                                                                    
	this->windowPos = this->pos();   
	this->mousePos = event->globalPos();  
	this->dPos = mousePos - windowPos;  
	return;
}  

void mainWindow::mouseMoveEvent(QMouseEvent *event){   
	this->move(event->globalPos() - this->dPos);  
	initializeRelativePos();
	return;
} 

void mainWindow::initializeRelativePos(){//@ A0105649B
	relativePos.setX(INITIAL_CHILD_POSX);
	relativePos.setY(INITIAL_CHILD_POSY);
	posDifference.setX(CHILD_POS_DIFFERENCEX);
	posDifference.setY(CHILD_POS_DIFFERENCEY);
	return;
}

void mainWindow::modifyRelativePos(){//@ A0105649B
	if(relativePos.y() <= MAX_CHILD_POSY){
		relativePos += posDifference;
	}
	else{
		initializeRelativePos();
	}
	return;
}

void mainWindow::setIsOnlyKeyword(){//@ A0105649B
	isOnlyKeyword = true;
	return;
}

void mainWindow::clearIsOnlyKeyword(){//@ A0105649B
	isOnlyKeyword = false;
	return;
}
